package com.syun.spring.ioc.core.annotation;

import com.syun.spring.ioc.core.bean.BeanDefinition;
import com.syun.spring.ioc.core.bean.BeanUtils;
import com.syun.spring.ioc.core.factory.ApplicationContext;
import org.apache.commons.lang.exception.ExceptionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class AnnotationConfigApplicationContextTwo implements ApplicationContext {

    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<String, BeanDefinition>();

    //一级缓存
    private Map<String, Object> singletonObjects = new HashMap<String, Object>();

    //二级缓存
    private Map<String, Object> earlySingletonObjects = new HashMap<String, Object>();

    private  final Class<?> configClass;

    public AnnotationConfigApplicationContextTwo(Class<?> configClass) {
        this.configClass = configClass;
        scanPackageClass();
        if(!beanDefinitionMap.isEmpty()){
            for(Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()){
                String id = entry.getKey();
                //在依赖注入的时候，部分bean可能会提前已创建
                if(!singletonObjects.containsKey(id)){
                    BeanDefinition bean = entry.getValue();
                    createBean(bean);
                }
            }
        }

    }

    /**
     * 扫描所有注解
     */
    private void scanPackageClass(){
        if(!configClass.isAnnotationPresent(ComponentScan.class)){
            return;
        }
        //获取注解中的扫描路径
        String basePackage = configClass.getDeclaredAnnotation(ComponentScan.class).basePackage();
        List<Class<?>> classes = ClassUtils.getClasses(basePackage);
        for(Class<?> cls : classes){
            BeanDefinition beanDefinition =  new BeanDefinition();
            //@Component或@Service
            if(cls.isAnnotationPresent(Component.class) || cls.isAnnotationPresent(Service.class)){
                String beanName = cls.getSimpleName();;
                //如果注解是@Service并且实现了接口，则取第一个接口为name
                if(cls.isAnnotationPresent(Service.class) && cls.getInterfaces().length>0){
                    beanName = cls.getInterfaces()[0].getSimpleName();
                }
                beanName = ClassUtils.toLowerCaseFirstOne(beanName);//首字母小写
                String className = cls.getName();
                beanDefinition.setBeanClass(cls);
                beanDefinition.setBeanName(beanName);
                beanDefinition.setClassName(className);
                if(beanDefinitionMap.containsKey(beanName)){
                    throw new RuntimeException("bean already exists: " + beanName);
                }
                beanDefinitionMap.put(beanName, beanDefinition);
            }
        }

    }

    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if(null == beanDefinition){
            throw new RuntimeException("no beanDefinition : " + beanName);
        }
        Object object = singletonObjects.get(beanName);
        if(null != object){
            return object;
        } else {
            return createBean(beanDefinition);
        }
    }

    /**
     * 从缓存获取单例bean
     * @param beanName
     * @return
     */
    public Object getSingletonObject(String beanName){
        /**
         * 1.先从一级缓存获取。
         * 2.当从一级缓存获取不到，则从二级缓存获取。
         */
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if(null == beanDefinition){
            throw new RuntimeException("no beanDefinition : " + beanName);
        }
        Object object = singletonObjects.get(beanName);
        if(object == null){
            object = earlySingletonObjects.get(beanName);
            if(object == null){
                object = createBean(beanDefinition);
            }
        }
        return object;
    }

    public Object createBean(BeanDefinition beanDefinition) {
        Class<?> beanClass = beanDefinition.getBeanClass();
        String beanName = beanDefinition.getBeanName();
        System.out.println("["+beanName+"] create start>>>>");
        Object object = null;
        // 实例化
        try {
            object = beanClass.newInstance();
            //将半成品存放到二级缓存
            earlySingletonObjects.put(beanName, object);
            System.out.println("["+beanName+"] 半成品保存到二级缓存");
        } catch (Exception e) {
            throw new RuntimeException("class instance fail:["+ beanName +"]" + "\n" + ExceptionUtils.getStackTrace(e));
        }

        for(Field field: beanClass.getDeclaredFields()){
            //属性赋值
            if(field.isAnnotationPresent(Value.class)){
                Value valueAnnotation = field.getAnnotation(Value.class);
                String value = valueAnnotation.value();
                //属性的setter方法
                Method setterMethod = BeanUtils.getSetterMethod(object, field.getName());
                Object val = null;
                switch (field.getType().getName()){
                    case "java.lang.Integer":
                        val = Integer.parseInt(value);
                        break;
                    case "java.lang.String":
                        val = value;
                        break;
                    case "java.lang.Float":
                        val = Float.parseFloat(value);
                        break;
                }
                try {
                    setterMethod.invoke(object, val);
                    System.out.println("["+beanName+"] set value >> " + field.getName() + ":" + val);
                } catch (Exception e) {
                    throw new RuntimeException("set value fail：[" + beanName + "." + field.getName() +"] " + "\n" +ExceptionUtils.getStackTrace(e));
                }
            }

            //依赖注入：@Autowired
            if(field.isAnnotationPresent(Autowired.class)){
                //从缓存获取bean
                Object ref = getSingletonObject(field.getName());
                if(ref == null){
                    throw new RuntimeException("set ref fail:["+field.getName()+"] is null");
                }
                field.setAccessible(true);
                try {
                    field.set(object, ref);
                    System.out.println("["+beanName+"] set DI >> " + field.getName() + ":" + ref.toString());
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("set ref fail:["+ beanName +"."+ field.getName() +"] " + "\n" +ExceptionUtils.getStackTrace(e));
                }
            }
        }
        System.out.println("["+beanName+"] end >> :" + object.toString());
        // 将完整的bean保存到一级缓存，并且删除二级缓存
        singletonObjects.put(beanName, object);
        earlySingletonObjects.remove(beanName);
        System.out.println("["+beanName+"] 完整版保存到一级缓存，并删除二级缓存");
        return object;
    }
}
